package com.ruyuan.rapid.core.helper;

import com.ruyuan.rapid.common.config.DynamicConfigManager;
import com.ruyuan.rapid.common.config.Rule;
import com.ruyuan.rapid.common.config.ServiceDefinition;
import com.ruyuan.rapid.common.config.ServiceInvoker;
import com.ruyuan.rapid.common.constants.BasicConst;
import com.ruyuan.rapid.common.constants.RapidConst;
import com.ruyuan.rapid.common.constants.RapidProtocol;
import com.ruyuan.rapid.common.enums.ResponseCode;
import com.ruyuan.rapid.common.exception.RapidNotFoundException;
import com.ruyuan.rapid.common.exception.RapidPathNoMatchedException;
import com.ruyuan.rapid.common.exception.RapidResponseException;
import com.ruyuan.rapid.common.util.AntPathMatcher;
import com.ruyuan.rapid.core.context.AttributeKey;
import com.ruyuan.rapid.core.context.RapidContext;
import com.ruyuan.rapid.core.context.RapidRequest;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import org.apache.commons.lang3.StringUtils;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * 解析请求信息,构建上下文对象
 * @author mac
 */
public class RequestHelper {

    private static final AntPathMatcher ANT_PATH_MATCHER = new AntPathMatcher();

    /**
     * 解析FullHttpRequest, 构建RapidContext上下文对象
     * @param request
     * @param ctx
     * @return
     */
    public static RapidContext doContext(FullHttpRequest request, ChannelHandlerContext ctx) {
        RapidRequest rapidRequest = doRequest(request, ctx);

        // 根据请求的uniqueId, 获取资源服务信息
        ServiceDefinition serviceDefinition = getServiceDefinition(rapidRequest);

        // 快速匹配路径失败的策略
        if (!ANT_PATH_MATCHER.match(serviceDefinition.getPatternPath(), rapidRequest.getPath())) {
            throw new RapidPathNoMatchedException();
        }

        // 根据请求对象获取服务定义对应的方法调用
        ServiceInvoker serviceInvoker = getServiceInvoker(rapidRequest, serviceDefinition);
        String ruleId = serviceInvoker.getRuleId();
        Rule rule = DynamicConfigManager.getInstance().getRule(ruleId);

        // 构建我们定义的RapidContext对象
        RapidContext rapidContext = new RapidContext.Builder()
                .setProtocol(serviceDefinition.getProtocol())
                .setRapidRequest(rapidRequest)
                .setNettyCtx(ctx)
                .setKeepAlive(HttpUtil.isKeepAlive(request))
                .setRule(rule).build();

        // 设置SR
        rapidContext.setSRTime(rapidRequest.getBeginTime());

        // 设置一些必要的上下文参数,用于后面使用
        putContext(rapidContext, serviceInvoker);
        return rapidContext;
    }

    /**
     * 设置必要的上下文属性
     * @param rapidContext
     * @param serviceInvoker
     */
    private static void putContext(RapidContext rapidContext, ServiceInvoker serviceInvoker) {
        switch (rapidContext.getProtocol()) {
            case RapidProtocol.HTTP:
                rapidContext.putAttribute(AttributeKey.HTTP_INVOKER, serviceInvoker);
                break;
            case RapidProtocol.DUBBO:
                rapidContext.putAttribute(AttributeKey.DUBBO_INVOKER, serviceInvoker);
                break;
            default:
                break;
        }
    }

    /**
     * 通过请求对象获取对应的服务定义信息
     * @param rapidRequest
     * @return
     */
    private static ServiceDefinition getServiceDefinition(RapidRequest rapidRequest) {
        // 就是在网关服务加载的时候获取
        ServiceDefinition serviceDefinition = DynamicConfigManager.getInstance().getServiceDefinition(rapidRequest.getUniqueId());
        if (serviceDefinition == null) {
            throw new RapidNotFoundException(ResponseCode.SERVICE_DEFINITION_NOT_FOUND);
        }
        return serviceDefinition;
    }

    private static RapidRequest doRequest(FullHttpRequest request, ChannelHandlerContext ctx) {
        HttpHeaders headers = request.headers();

        // 从header头获取必须要传入的关键属性uniqueId
        String uniqueId = headers.get(RapidConst.UNIQUE_ID);
        if (StringUtils.isBlank(uniqueId)) {
            throw new RapidResponseException(ResponseCode.REQUEST_PARSE_ERROR_NO_UNIQUEID);
        }

        String host = headers.get(HttpHeaderNames.HOST);
        HttpMethod method = request.method();
        String uri = request.uri();
        String clientIp = getClientIp(ctx, request);
        String contentType = HttpUtil.getMimeType(request) == null ? null : HttpUtil.getMimeType(request).toString();
        Charset charset = HttpUtil.getCharset(request, StandardCharsets.UTF_8);

        RapidRequest rapidRequest = new RapidRequest(uniqueId, charset, clientIp, host, uri, method, contentType, headers, request);
        return rapidRequest;
    }

    private static String getClientIp(ChannelHandlerContext ctx, FullHttpRequest request) {
        String xForwardedValue = request.headers().get(BasicConst.HTTP_FORWARD_SEPARATOR);
        String clientIp = null;
        if (StringUtils.isNotEmpty(xForwardedValue)) {
            List<String> values = Arrays.asList(xForwardedValue.split(","));
            if (values.size() >= 1 && StringUtils.isNotBlank(values.get(0))) {
                clientIp = values.get(0);
            }
        }
        if (clientIp == null) {
            InetSocketAddress inetSocketAddress = (InetSocketAddress) ctx.channel().remoteAddress();
            clientIp = inetSocketAddress.getAddress().getHostAddress();
        }
        return clientIp;
    }

    private static ServiceInvoker getServiceInvoker(RapidRequest rapidRequest, ServiceDefinition serviceDefinition) {
        Map<String, ServiceInvoker> invokerMap = serviceDefinition.getInvokerMap();
        ServiceInvoker serviceInvoker = invokerMap.get(rapidRequest.getPath());
        if (serviceInvoker == null) {
            throw new RapidNotFoundException(ResponseCode.SERVICE_INVOKER_NOT_FOUND);
        }
        return serviceInvoker;
    }
}
